package com.dgut.edu.cn.design.mode.log;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.JSONSerializer;
import com.alibaba.fastjson.serializer.SerializeFilter;
import com.alibaba.fastjson.serializer.SerializeWriter;
import com.alibaba.fastjson.serializer.SerializerFeature;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.MDC;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class LogMsgKit {

	private static final String LOGGER_TRACE_ID = "trace_id";




	private static final SerializerFeature[] FEATURES = { //
			SerializerFeature.UseISO8601DateFormat, // 日期打印为立即可见的日期，而不是时间戳
			SerializerFeature.WriteNonStringKeyAsString, // 避免 key 为 number 类型的打印出问题
	};

	private String event;

	private SerializeFilter filter;

	private JSONObject msgJson = new JSONObject(4);

	private LogMsgKit() {
	}

	public static LogMsgKit of() {
		return new LogMsgKit();
	}

	public static LogMsgKit of(String event) {
		LogMsgKit of = of();
		return of.event(event);
	}

	/** 日志事件 */
	public LogMsgKit event(String event) {
		this.event = event;
		return this;
	}

	/** 将 Map 的内容设置给 msgJson */
	public LogMsgKit map(Map<?, ?> map) {
		if (map != null) {
			map.forEach(this::pair);
		}
		return this;
	}

	/** 日志中的 msgJson 字段 */
	public LogMsgKit json(JSONObject json) {
		if (json != null) {
			this.msgJson = json;
		}
		return this;
	}

	/** 设置 msgJson 键值对内容，避免 null key 和过长的异常堆栈内容 */
	public LogMsgKit pair(Object key, Object value) {
		this.msgJson.put(String.valueOf(key), handleValue(value));
		return this;
	}

	/** 设置 msgJson 键值对内容，{@link #pair(Object, Object)} 的简写 */
	public LogMsgKit p(Object key, Object value) {
		return pair(key, value);
	}

	/** 日志消息 */
	private LogMsgKit msg(String msg) {
		msgJson.put("$msg", msg);
		return this;
	}

	/** 构建日志的 msg 字段并返回整个日志内容 */
	public String end(String msg) {
		return msg(msg).end();
	}

	/**
	 */
	public String end() {
		if (event == null) {
			event = "empty_event";
		}

		// 改动，让 json 字段只记录 trace_id
		String jsonStr;
		String traceId = MDC.get("traceId");
		if (StringUtils.isNotEmpty(traceId)) {
			JSONObject jsonObj = new JSONObject(1);
			jsonObj.put(LOGGER_TRACE_ID, traceId);
			jsonStr = jsonObj.toString();
		} else {
			jsonStr = "{}";
		}

		return "[" + event + "] json=" + jsonStr + " msg=" + msgJson();
	}

	private String msgJson() {
		try (SerializeWriter out = new SerializeWriter(null, JSON.DEFAULT_GENERATE_FEATURE, FEATURES)) {
			final JSONSerializer serializer = new JSONSerializer(out);
			if (filter != null) {
				serializer.addFilter(filter);
			}
			serializer.write(msgJson);
			return out.toString();
		}
	}


	/** 避免日志内容中堆积异常堆栈 */
	private Object handleValue(Object value) {
		if (value instanceof Throwable) {
			return asTraceList((Throwable) value);
		}
		return value;
	}

	/** 异常转成堆栈内容 */
	private List<String> asTraceList(Throwable throwable) {
		List<String> result = new ArrayList<>();
		result.add(throwable.getClass().getName() + ":" + throwable.getMessage());
		StackTraceElement[] stackTraces = throwable.getStackTrace();
		for (int i = 0, max = Math.min(stackTraces.length, 100); i < max; i++) {
			result.add(stackTraces[i].toString());
		}
		return result;
	}

}
